package wpwork.config;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.strategy.SaStrategy;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import wpwork.base.UserInfo;
import wpwork.consts.AuthConsts;
import wpwork.database.util.NotTenantPluginHolder;
import wpwork.util.StringUtil;
import wpwork.util.TenantProvider;
import wpwork.util.UserProvider;
import wpwork.util.data.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.*;
import java.io.IOException;
import java.util.List;
import java.util.function.BiFunction;

/**
 *
 * @author wpwork开发平台组
 * @copyright 武汉维普科技有限公司
 */
@Slf4j
@Configuration
public class SecurityConfiguration implements WebMvcConfigurer {


    @Autowired
    private ConfigValueUtil configValueUtil;

    /**
     * 注册sa-token的拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册路由拦截器，自定义验证规则
        // 这里只处理登录缓存清理 具体拦截在网关处理
        registry.addInterceptor(new SaInterceptor((handler) -> {

        }).isAnnotation(configValueUtil.isEnablePreAuth())).addPathPatterns("/**");
        //接口鉴权忽略管理员、内部请求
        BiFunction<List<String>, String, Boolean> oldCheckFunc = SaStrategy.me.hasElement;
        SaStrategy.me.hasElement = (list, element) -> {
            //启用之后才验证
            if(configValueUtil.isEnablePreAuth()){
                UserInfo userInfo = UserProvider.getUser();
                //未获取到用户信息返回false
                if(StringUtil.isEmpty(userInfo.getUserId())){
                    return false;
                }
                //管理员返回true
                if(userInfo.getIsAdministrator()){
                    return true;
                }
                boolean result = oldCheckFunc.apply(list, element);
                //如果鉴权失败， 检测是否来自内部请求
                if(!result){
                    String innerToken = SaHolder.getRequest().getHeader(AuthConsts.INNER_TOKEN_KEY);
                    //来自内部请求(非网关) 无需鉴权
                    if(UserProvider.isValidInnerToken(innerToken)){
                        result = true;
                    }
                }
                return result;
            }
            return true;
        };
    }



    @Bean
    public Filter getClearThreadContextFilter(){
        return new ClearThreadContextFilter().setBeforeAuth(obj -> {
            //验证请求来源, 网关, Feign
            if(configValueUtil.isEnableInnerAuth()) {
                SaRouter.match("/favicon.ico").stop();
                SaRouter.match("/**").match(r->{
                    String innerToken = SaHolder.getRequest().getHeader(AuthConsts.INNER_TOKEN_KEY);
                    String innerGatewayToken = SaHolder.getRequest().getHeader(AuthConsts.INNER_GATEWAY_TOKEN_KEY);
                    if(!UserProvider.isValidInnerToken(innerGatewayToken) && !UserProvider.isValidInnerToken(innerToken)){
                        log.error("非法请求: {}, {}", SaHolder.getRequest().getRequestPath(), innerToken);
                        return true;
                    }
                    return false;
                }).back("非法请求, 缺少认证信息");
            }
        }).setAuth(obj -> {
            //执行Token续期, 存用户信息至本地缓存后续无需重新获取
            UserProvider.renewTimeout();
            //设置租户信息
            if(configValueUtil.isMultiTenancy()) {
                UserInfo userInfo = UserProvider.getUser();
                if (StringUtil.isNotEmpty(userInfo.getTenantId()) && StringUtil.isNotEmpty(userInfo.getTenantDbConnectionString())) {
                    DataSourceContextHolder.setDatasource(userInfo.getTenantId(), userInfo.getTenantDbConnectionString(), userInfo.isAssignDataSource());
                }
            }
        }).addInclude("/**");
    }

    /**
     * 线程缓存清理
     */
    @Order(-99)
    public static class ClearThreadContextFilter extends SaServletFilter {

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            try {
                super.doFilter(request, response, chain);
            }finally {
                //清除线程缓存
                UserProvider.clearLocalUser();
                TenantProvider.clearBaseSystemIfo();
                DataSourceContextHolder.clearDatasourceType();
                DynamicDataSourceContextHolder.clear();
                NotTenantPluginHolder.clearNotSwitchFlag();
            }
        }

    }


}
